/**
 * Writed by yura and mak
 */

package wz.legend.handlers.steps;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import wz.legend.Context;
import wz.legend.HttpContext;
import wz.legend.util.ItemUtils;
import wz.legend.util.UriConstants;
import wz.legend.util.Utils;


public class SearchGoalStep extends AbstractStep {
    private static final String TABLE = "//TABLE[@class='coll w100 p6h p2v']";
    private static final String USERS = "./TBODY/TR/TD/A/B";

    private static final Logger LOG = Logger.getLogger(SearchGoalStep.class);
    public static final String ATTACK_GOAL = "fight.goal";

    private int startDistance = 500;
    private long newBotsCount = 2;
    private long botsCount = 4;
    private String botName = "";
    private boolean waitSecondUpdate = true;

    private List<Node> lastBots = new ArrayList<Node>();
    private XPath path = XPathFactory.newInstance().newXPath();

    public void execute(Context context) {
        LOG.debug("SearchGoalStep START");
        state = State.START;
    }

    public void handle(HttpContext httpContext, Context context) {
        try {
            if (httpContext != null) {
                String uri = httpContext.getRequest().getRequestLine().getUri();
                if (uri.contains(UriConstants.HUNT_CONF_PHP)
                        && state != State.UNKNOWN) {
                    if (state != State.ACTIVE) {
                        state = State.ACTIVE;
                        LOG.debug("SearchGoalStep ACTIVE");
                    }
                    Node bestBot = findBestItem(httpContext
                            .getResponseDocument());
                    if (bestBot != null) {
                        context.addProperty(ATTACK_GOAL, bestBot);
                        Point point = Utils.getItemPosition(bestBot);
                        LOG.debug("SearchGoalStep STOPED item found x="
                                + point.x + " y=" + point.y);
                        state = State.STOPED;
                    } else {
                        context.removeProperty(ATTACK_GOAL);
                    }
                }
            }
        } catch (Exception e) {
            LOG.error(e, e);
            throw new RuntimeException(e);
        }
    }

    public Node findBestItem(Document document) throws Exception {
        List<Node> allBots = getBotsList(document);
        List<Node> bots = getNotFightingBotsList(document);
        List<Node> newBots = new ArrayList<Node>();

        if (lastBots.size() == 0 && waitSecondUpdate) {
            bots.clear();
        }

        for (Node node : bots) {
            if (!contansInLastBots(node)) {
                newBots.add(node);
                LOG.debug("new bot found id = "
                        + Utils.getAttributeInt(node, "id") + " x="
                        + Utils.getAttributeInt(node, "x") + " y="
                        + Utils.getAttributeInt(node, "y"));
            }
        }
        LOG.debug("newBot size = " + newBots.size() + " bot size = "
                + bots.size() + " allbots size = " + allBots.size());

        updateLastBots(allBots);

        if (bots.size() < newBotsCount) {
            return null;
        }
        Node bestNode = ItemUtils.findBestItem(newBots, allBots, startDistance,
                true);

        if (bestNode == null && bots.size() > botsCount) {
            bestNode = ItemUtils.findBestItem(bots, allBots, startDistance,
                    true);
        }
        return bestNode;
    }

    List<Node> parseFightInfo(Document document) throws Exception {
        Node nodeTable = (Node) path.evaluate(TABLE, document,
                XPathConstants.NODE);

        NodeList userNodeList = (NodeList) path.evaluate(USERS, nodeTable,
                XPathConstants.NODESET);

        List<Node> list = Utils.convertNodeList(userNodeList);
        for (Node node : list) {
            System.out.println(node.getTextContent());
        }
        return list;
    }

    private boolean contansInLastBots(Node bot) {
        int botId = Utils.getAttributeInt(bot, "id");
        for (Node node : lastBots) {
            if (botId == Utils.getAttributeInt(node, "id")) {
                return true;
            }
        }
        return false;
    }

    public List<Node> getBotsList(Document document) throws Exception {
        StringBuffer query = new StringBuffer();
        query
                .append("/hunt/bots/bot[@x>0 and @x<1500 and @y>0 and @y<1500 and @sk!=2001]");
        return Utils.convertNodeList((NodeList) path.evaluate(query.toString(),
                document, XPathConstants.NODESET));
    }

    public List<Node> getNotFightingBotsList(Document document)
            throws Exception {
        StringBuffer query = new StringBuffer();
        query
                .append("/hunt/bots/bot[@name='"
                        + botName
                        + "' and @x>0 and @x<1500 and @y>0 and @y<1500 and @fight_id=0]");
        return Utils.convertNodeList((NodeList) path.evaluate(query.toString(),
                document, XPathConstants.NODESET));
    }

    public void updateLastBots(List<Node> bots) throws Exception {
        lastBots.clear();
        lastBots.addAll(bots);
    }

    @Override
    public void reset() {
        lastBots.clear();
        super.reset();
    }

    public int getStartDistance() {
        return startDistance;
    }

    public void setStartDistance(int startDistance) {
        this.startDistance = startDistance;
    }

    public long getNewBotsCount() {
        return newBotsCount;
    }

    public void setNewBotsCount(long newBotsCount) {
        this.newBotsCount = newBotsCount;
    }

    public long getBotsCount() {
        return botsCount;
    }

    public void setBotsCount(long botsCount) {
        this.botsCount = botsCount;
    }

    public String getBotName() {
        return botName;
    }

    public void setBotName(String botName) {
        this.botName = botName;
    }

    public boolean isWaitSecondUpdate() {
        return waitSecondUpdate;
    }

    public void setWaitSecondUpdate(boolean waitSecondUpdate) {
        this.waitSecondUpdate = waitSecondUpdate;
    }

    public List<Node> getLastBots() {
        return lastBots;
    }

    public void setLastBots(List<Node> lastBots) {
        this.lastBots = lastBots;
    }
}
